/**
 * @description 文件自动同步脚本(2024/10/08)
 *  使用方法：
 *  - 第一次使用请先在远端服务器手动创建SYNC_FULLPATH相对应的文件夹
 *    - 创建.env.local文件并配置环境变量
 *      - SERVER_IP     服务器IP
 *      - SSH_USERNAME  服务器用户名
 *      - SSH_PWD       服务器密码
 *      - SSH_PORT      服务器SSH端口
 *      - SYNC_FULLPATH 远端同步文件夹绝对路径（linux路径）
 *  - 配置 excludePathList 用于忽略指定路径的文件或文件夹,支持深层路径
 *  - 使用 npm run sync 命令完成同
 */
const fs = require('fs');
const path = require('path');
const dotenv = require('dotenv');
const { Client } = require('ssh2');
dotenv.config({ path: `.env.local`, override: true });
const { SSH_PORT, SERVER_IP, SSH_USERNAME, SSH_PWD, SYNC_FULLPATH } = process.env;

const remoteBasePath = SYNC_FULLPATH;

const sshConfig = {
  host: SERVER_IP,
  port: SSH_PORT,
  username: SSH_USERNAME,
  password: SSH_PWD,
};

const excludePathList = [
  '/.husky',
  '/.git',
  '/node_modules',
  '/public',
  '/workflow.js',
  '/.browserslistrc',
  '/.env.local',
  '/.eslintrc-auto-import.json',
  '/.eslintrc.js',
  '/.gitignore',
  '.prettierignore',
  '/babel.config.js',
  'commitlint.config.js',
  '/jsconfig.json',
  '/package-lock.json',
  '/package.json',
  '/README.md',
  '/vue.config.js',
  '/workflow.js',
];

const sshClientInstance = new Client();
let sftpInstance = null;
sshClientInstance.connect(sshConfig);

let uploadTaskCount = 0;
let uploadTaskMap = new Map([]);
let TASK_INIT = 0;
let TASK_SUCCESS = 1;
let TASK_FAIL = 2;

sshClientInstance.on('ready', () => {
  console.log('🟢SSH :: ready');
  sshClientInstance.sftp(async (err, sftp) => {
    if (err) console.log(err);
    sftpInstance = sftp;
    console.log('🟢SFTP :: ready');

    const outerFiles = fs.readdirSync(__dirname);
    await Promise.all(
      outerFiles.map(async (fileName) => {
        await walk('.', fileName);
      })
    );
    uploadTaskCount = uploadTaskMap.size;
    await Promise.all(
      Array.from(uploadTaskMap.keys()).map(async (uploadPath) => {
        await create_upload_task(uploadPath);
      })
    );

    console.log('🔻同步脚本执行完成，自动关闭中..');
    sshClientInstance.end();
  });
});

function create_upload_task(rel_localFilePath) {
  return new Promise((resolve, reject) => {
    const localFilePath = path.resolve(__dirname, rel_localFilePath.slice(1));
    const remoteFilePath = `${remoteBasePath}${rel_localFilePath.replace(/\\/g, '/')}`;
    sftpInstance.fastPut(localFilePath, remoteFilePath, (err) => {
      uploadTaskCount--;
      if (err) {
        console.log('❌文件同步失败..', err, localFilePath, remoteFilePath);
        uploadTaskMap.set(rel_localFilePath, TASK_FAIL);
        reject(err);
      } else {
        console.log('✅文件同步成功..', remoteFilePath);
        uploadTaskMap.set(rel_localFilePath, TASK_SUCCESS);
        resolve(remoteFilePath);
      }
      console.log(`总任务数量:${uploadTaskMap.size}, 剩余任务数量:${uploadTaskCount}`);
    });
  });
}

async function walk(basePath, fileName) {
  const rel_localFilePath = `${basePath.split('.')[1]}/${fileName}`;
  const isDir = fs.statSync(path.resolve(basePath, fileName)).isDirectory();
  const walkNextDir = async (basePath, fileName) => {
    const files = fs.readdirSync(path.resolve(basePath, fileName));
    await Promise.all(
      files.map(async (newFileName) => {
        await walk(`${basePath}/${fileName}`, newFileName);
      })
    );
  };

  if (!excludePathList.includes(rel_localFilePath)) {
    const remoteDirPath = remoteBasePath + rel_localFilePath;
    if (isDir) {
      try {
        const dirListRes = await new Promise((resolve) => {
          // MARK：这里不要捕获异常，使用list的值来判断读取结果
          sftpInstance.readdir(remoteDirPath, (readdirErr, list) => {
            resolve(list);
          });
        });
        if (dirListRes == undefined) {
          await new Promise((resolve, reject) => {
            sftpInstance.mkdir(remoteDirPath, (err) => {
              if (err) {
                console.log(`❌创建远端文件夹：${remoteDirPath}失败`, err);
                reject(`❌创建远端文件夹：${remoteDirPath}失败-${err}`);
              } else {
                console.log(`📂新文件夹：${remoteDirPath}创建成功..`);
                resolve(`📂新文件夹：${remoteDirPath}创建成功..`);
              }
            });
          });
        }
        await walkNextDir(basePath, fileName);
      } catch (error) {
        console.error('Error processing directory:', error);
      }
    } else {
      uploadTaskMap.set(rel_localFilePath, TASK_INIT);
      return;
    }
  }
}
